package br.ufrgs.seguranca.cryptography;

import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class RSAKeyGenerator {
	
	BigInteger p, q, n, e, d;	
	List<Integer> nDivisors = new LinkedList<Integer>();
	
	
	public RSAKeyGenerator(BigInteger p, BigInteger q) {
		setP(p);
		setQ(q);
		// 2 - Calcule o "n" do RSA multiplicando estes dois primos (o que está neste arquivo e o que você gerou).
		n = p.multiply(q);
	}
	
	private void findNdivisors()
	{
		try {
			BigInteger zero = new BigInteger("0");
			BigInteger two = new BigInteger("2");
			BigInteger three = new BigInteger("3");
			BigInteger five = new BigInteger("5");
			BigInteger seven = new BigInteger("7");
			
			if (n.divideAndRemainder(two)[1] == zero) {
				nDivisors.add(two.intValue());
			}
			
			if (n.divideAndRemainder(three)[1] == zero) {
				nDivisors.add(three.intValue());
			}
			
			if (n.divideAndRemainder(five)[1] == zero) {
				nDivisors.add(five.intValue());
			}
			
			if (n.divideAndRemainder(seven)[1] == zero) {
				nDivisors.add(seven.intValue());
			}
		} catch (Exception e) {
			System.out.println(">>>>>>>>>>>>>>> RASKeyGenerator.findDivisors()");
			e.printStackTrace();
		}
	}
	
	// 3.1 - Calcule (n,e), a tua chave pública, de acordo com o indicado pelo RSA.
	public void generateEandD()
	{	
		try {
			findNdivisors();
			Integer eCandidate;
			Random random = new Random();
			
			while(true) {
				
				eCandidate = random.nextInt(64000);
			
				for(Integer i : nDivisors) {
					if (eCandidate % i == 0) {
						System.out.println(eCandidate.toString() + "not valid.");
						break;
					}
				}
				
				e = new BigInteger(eCandidate.toString());
				
				try
				{
					generateD();
					break;
				}
				catch(Exception ex) {
					System.out.println(e.toString() + " rejected");
				}
				
			}
			random.nextInt();
		} catch (Exception e) {
			System.out.println(">>>>>>>>>>>>>>> RASKeyGenerator.generateE()");
			e.printStackTrace();
		}
	}
	
	// 3.2 - Calcule (n,d), a tua chave privada, de acordo com o indicado pelo RSA.
	private void generateD() {
		// d = e^-1 mod (p-1)*(q-1)		
		BigInteger one = new BigInteger("1");
		d = e.modInverse(p.subtract(one).multiply(q.subtract(one)));		
	}
	
	public BigInteger getP() {
		return p;
	}

	public void setP(BigInteger p) {
		this.p = p;
	}

	public BigInteger getQ() {
		return q;
	}

	public void setQ(BigInteger q) {
		this.q = q;
	}

	public BigInteger getN() {
		return n;
	}

	public void setN(BigInteger n) {
		this.n = n;
	}

	public BigInteger getE() {
		return e;
	}

	public void setE(BigInteger e) {
		this.e = e;
	}

	public BigInteger getD() {
		return d;
	}

	public void setD(BigInteger d) {
		this.d = d;
	}
	
	
}


/*

> Bob escolhe 'p' = 101 e 'q' = 113
> Então 'n' = 11413 e '*(n)' = 100 * 112 = 11200
> Como 11200 = 2⁶ 5² 7, serve qualquer número que não 
seja divisível por 2, 5, 7 para ser 'e'
> Na prática 'e' é randômico e testa-se por mdc(e, *(n)) = 1
> Bob escolhe 'e' = 3533
> Então 'd' = 'e'^-1 mod ('p'-1)*('q'-1) = 6597
> A chave pública de Bob é  (3533, 11413)
> Alice deseja cifrar 9726 para Bob
> Alice calcula 9726^3533 mod 11413 = 5671 (message^'e' mod )
> Para decifrar, Bob calcula 5761^6597 mod 11413 = 9726



Vamos precisar de dois primos. Um deles eu já gerei:
5700734181645378434561188374130529072194886062007
=> yeah yeah 1363005552434666078217421284621279933627102780881053358473 
Vou enviar o segundo primo no segundo arquivo.

Se por acaso você não conseguir ou não quiser decifrar o segundo arquivo, então faça o seguinte:

1 - Gere um segundo primo (maior que o primo dado acima, por favor) - para isto, é bom usar uma função de teste de primalidade, ou então implementar um teste determinístico.

2 - Calcule o "n" do RSA multiplicando estes dois primos (o que está neste arquivo e o que você gerou).

3 - Calcule (n,e) e (n,d), as tuas chaves pública e privada, de acordo com o indicado pelo RSA.

4 - Assine o "desafio" abaixo com a tua chave secreta:
60197547605833586049069265075746914070

Isso é bem seguro. Tão seguro que eu até lanço um desafio aqui: se algum aluno estiver lendo isto, então siga as mesmas instruções e coloque no Moodle a chave pública (o par (n,e)) e o desafio assinado (ou seja, o resultado de (desafio^d mod n))

E sabe de uma coisa? Descobri um ótimo gerador de randômicos. Daqui para a frente, vou usar somente ele:
                         2^x . 3^y . 5^w . 7^z
onde x, y, w e z são inteiros positivos maiores que zero.*/ 


